import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

enum SKLOGLevel {
  debug(SKLOGColors.cyan, 0),
  info(SKLOGColors.brightGreen, 1),
  warning(SKLOGColors.brightYellow, 2),
  error(SKLOGColors.brightRed, 3),
  ;

  final SKLOGColors color;
  final int level;
  const SKLOGLevel(this.color, this.level);
}

enum SKLOGColors {
  black('\x1B[30m', Colors.black),
  red('\x1B[31m', Colors.red),
  green('\x1B[32m', Colors.green),
  yellow('\x1B[33m', Colors.yellow),
  blue('\x1B[34m', Colors.blue),
  magenta('\x1B[35m', Colors.purple),
  cyan('\x1B[36m', Colors.cyan),
  white('\x1B[37m', Colors.white),
  brightBlack('\x1B[90m', Color(0xFF9E9E9E)),
  brightRed('\x1B[91m', Color(0xFFFF5722)),
  brightGreen('\x1B[92m', Color(0xFF8BC34A)),
  brightYellow('\x1B[93m', Color(0xFFFFFF00)),
  brightBlue('\x1B[94m', Color(0xFF2196F3)),
  brightMagenta('\x1B[95m', Color(0xFFE040FB)),
  brightCyan('\x1B[96m', Color(0xFF00E5FF)),
  brightWhite('\x1B[97m', Color(0xFFFFFFFF)),
  reset('\x1B[0m', Colors.black),
  ;

  final String colorStr;
  final Color color;
  const SKLOGColors(this.colorStr, this.color);
}

SKLOGLevel defaultLevel = kDebugMode ? SKLOGLevel.debug : SKLOGLevel.info;

class _BlankLog {
  _BlankLog operator <<(dynamic message) => this;
  _BlankLog call(dynamic message) => this;
}

class SKLOG extends _BlankLog {
  final bool printAnyway;
  final SKLOGLevel level;
  final SKLOGLevel _printlevel = defaultLevel;
  final SKLOGColors? color;

  int _printLine = 1;
  static bool globalFullPath = true;
  static bool globalPrintFlag = true;

  int i = 0;
  String prefix = '';
  List<String> list = [];

  get _shouldPrint =>
      printAnyway || (globalPrintFlag && _printlevel.level <= level.level);

  SKLOG({
    this.printAnyway = false,
    this.level = SKLOGLevel.info,
    this.color,
    String? text,
    bool? fullPath,
    bool newLine = false,
    int printLineIndex = 1,
  }) {
    if (_shouldPrint) {
      _printLine = printLineIndex;
      fullPath ??= SKLOG.globalFullPath;
      final stackTrace = StackTrace.current;
      final fileLine = stackTrace.toString().split('\n')[_printLine];
      final regexp =
          RegExp(r'^#\d+\s+(.+) \((.+\/)(.+:\d+:?\d+?)\)$', multiLine: true);
      RegExpMatch result = regexp.firstMatch(fileLine)!;
      String funName = result.group(1)!;
      String funNamePrefix = result.group(2)!;
      String fileInfo = result.group(3)!;
      prefix =
          '[${fullPath ? funNamePrefix : ''}$fileInfo] [$funName]:${newLine ? '\n     ' : ''}   ';
      _add(prefix);
      if (text != null) {
        _add(text);
      }
    }
  }

  void _add(dynamic obj) {
    String str = obj.toString();
    list.add(str);
    i = list.length;
    final count = i;
    Future.delayed(Duration.zero, () {
      if (count == i) _log();
    });
  }

  void _log() {
    String color = this.color?.colorStr ?? level.color.colorStr;
    list = list.map(
      (e) {
        var str = e;
        if (e.contains('\n')) str = e.replaceAll('\n', '\n        ');
        return str;
      },
    ).toList();
    print("$color${list.join(' ')}${SKLOGColors.reset.colorStr}");
    // list.clear();
  }

  @override
  _BlankLog operator <<(dynamic message) {
    if (_shouldPrint) {
      _add(message);
      return this;
    } else {
      return _BlankLog();
    }
  }

  @override
  _BlankLog call(dynamic message) {
    if (_shouldPrint) {
      _add(message);
      return this;
    } else {
      return _BlankLog();
    }
  }

  static SKLOG get debug => SKLOG(level: SKLOGLevel.debug, printLineIndex: 2);

  static SKLOG get warning =>
      SKLOG(level: SKLOGLevel.warning, printLineIndex: 2);

  static SKLOG get info => SKLOG(level: SKLOGLevel.info, printLineIndex: 2);

  static SKLOG get error => SKLOG(level: SKLOGLevel.error, printLineIndex: 2);

  static void forbiddenPrint() {
    globalPrintFlag = false;
  }

  static void allowPrint() {
    globalPrintFlag = true;
  }

  static void setDefaultLevel(SKLOGLevel level) {
    defaultLevel = level;
  }
}
